package polytime.ui.popup.actions;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.HashMap;

import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.mwe.utils.StandaloneSetup;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.resource.XtextResourceSet;
import org.xtext.example.mydsl.MyDslStandaloneSetup;
import org.xtext.example.mydsl.myDsl.Abs;
import org.xtext.example.mydsl.myDsl.App;
import org.xtext.example.mydsl.myDsl.FileSF;
import org.xtext.example.mydsl.myDsl.Let;
import org.xtext.example.mydsl.myDsl.Nrif;
import org.xtext.example.mydsl.myDsl.Prog;
import org.xtext.example.mydsl.myDsl.T;
import org.xtext.example.mydsl.myDsl.T1;
import org.xtext.example.mydsl.myDsl.T2;
import org.xtext.example.mydsl.myDsl.T3;
import org.xtext.example.mydsl.myDsl.Tapp;
import org.xtext.example.mydsl.myDsl.Term;
import org.xtext.example.mydsl.myDsl.Trif;
import org.xtext.example.mydsl.myDsl.Uq;
import org.xtext.example.mydsl.myDsl.Var;

import bridge.Controller;

import com.google.inject.Injector;

public class Interprete implements IObjectActionDelegate {
	private Shell shell;
	private String fileName;
	private String workingPath;
	private String uriPrefix;
	private PrintStream stdout = System.out;
	private PrintStream silence = new PrintStream(new SilenceIO());
	private String userhome = System.getProperty("user.home");

	private class SilenceIO extends OutputStream {
		@Override
		public void write(int b) throws IOException {
		}
	}
	
	/**
	 * Constructor for Action1.
	 */
	public Interprete() {
		super();
	}

	/**
	 * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart)
	 */
	public void setActivePart(IAction action, IWorkbenchPart targetPart) {
		shell = targetPart.getSite().getShell();
	}

	/**
	 * @see IActionDelegate#run(IAction)
	 */
	public void run(IAction action) {
		try {
			if (!checkUtility()) {
				MessageDialog
						.openError(shell, "File not found",
								"aggiungi inf_dlal a questo path " + userhome
										+ "/util");
			}
		} catch (IOException e) {
			MessageDialog.openError(shell, "File not found", e.toString());
			return;
		}
		try {
			FileSF file = getFile();
			if(file instanceof Prog){
				Term main = ((Prog) file).getMain();
				String dlalStr = termToString(main);
				String lciStr = termToStringLci(main);
				Controller.infDlal(dlalStr);
				String temp =Controller.lciInterpreter(lciStr);
				String type = typeTerm();
				MessageDialog.openInformation(shell, "result", dlalStr+"\n"+type+"\n"+lciStr+"\n\n"+temp);
			}
		} catch (IOException e) {
			MessageDialog.openError(shell, "File not found", e.toString());
			return;
		} catch (InterruptedException e) {
			MessageDialog.openError(shell, "File not found", e.toString());
		} catch (Exception e) {
			MessageDialog.openError(shell, "File not found", e.toString());
		}
		

	}

	/**
	 * @see IActionDelegate#selectionChanged(IAction, ISelection)
	 */
	/**
	 * @see IActionDelegate#selectionChanged(IAction, ISelection)
	 */
	public void selectionChanged(IAction action, ISelection selection) {
		fileName = toFileName(selection.toString());
		workingPath = toWorkingPath(selection.toString());
		uriPrefix = toUriPrefix(selection.toString());
	}
	

	/* SERVICE METHODS */
	private String toFileName(String name) {
		int idx = getLastSeparatorIndex(name);

		if (idx == -1)
			return "";

		return name.substring(idx + 1, name.length() - 1);
	}

	private String toWorkingPath(String name) {
		IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
		String path = root.getLocation().toString() + "/";

		int idx = getLastSeparatorIndex(name);
		if (idx == -1)
			return "";

		return path + name.substring(3, idx) + "/";
	}

	private String toUriPrefix(String name) {
		int idx = name.indexOf('/');
		if (idx == -1)
			return "";

		return "platform:/resource"
				+ name.substring(idx, name.lastIndexOf('/')) + "/";
	}

	private int getLastSeparatorIndex(String name) {
		int idx = name.length() - 1;

		while (name.charAt(idx) != '/') {
			idx--;
			if (idx < 0)
				return -1;
		}

		return idx;
	}

	private void echo(boolean value) {
		if (value) {
			System.setOut(stdout);
		} else {
			System.setOut(silence);
		}
	}

	
	/**
	 * restituisce la radice dell'albero sintattico
	 * 
	 * @return la radice dell'albero sintattico
	 * @throws IOException
	 */
	private FileSF getFile() throws IOException {
		echo(false);
		StandaloneSetup standalone = new StandaloneSetup();
		standalone.setPlatformUri(workingPath);
		Injector injector =  new MyDslStandaloneSetup()
				.createInjectorAndDoEMFRegistration();
		XtextResourceSet resourceSet = ((com.google.inject.Injector) injector)
				.getInstance(XtextResourceSet.class);
		resourceSet.addLoadOption(XtextResource.OPTION_RESOLVE_ALL,
				Boolean.TRUE);

		Resource resource = resourceSet.createResource(URI.createURI(uriPrefix
				+ fileName));
		InputStream in = new FileInputStream(workingPath + fileName);

		resource.load(in, resourceSet.getLoadOptions());

		if (resource.getContents().size() == 0) {
			MessageDialog.openError(shell, "File not found",
					"The file is empty: no folder has been generated");
			return null;
		}
		FileSF model = (FileSF) resource.getContents().get(0);
		echo(true);
		return model;
	}	
	


	/**
	 * restituisce la stringa che rappresenta il termine Term t il termine
	 * @return la stringa che rappresenta il termine
	 */
	private String termToString(Term t) {

		if (t instanceof Var) {
			return ((Var) t).getName();

		} else if (t instanceof Abs) {
			return "\\(" + (((Abs) t).getParam()) + ":"
					+ typeToString(((Abs) t).getType()) + ")."
					+ termToString(((Abs) t).getBody());
		} else if (t instanceof App) {
			return "(" + termToString(((App) t).getT1()) + ")"
					+ termToString(((App) t).getT2());
		} else if (t instanceof Uq) {
			return "/\\" + (((Uq) t).getVar()) + "."
					+ termToString(((Uq) t).getTerm());
		} else if (t instanceof Tapp) {
			return "(" + termToString(((Tapp) t).getTerm()) + ")"
					+ typeToString(((Tapp) t).getType());
		} else if (t instanceof Nrif) {
			return termToString(((Nrif) t).getNome().getTerm());
		} else if (t instanceof Let) {
			return "\\((" + (((Let) t).getVar()) + ":"
					+ typeToString((((Let) t).getType())) + ")."
					+ termToString(((Let) t).getT2()) + ")"
					+ termToString(((Let) t).getT1());
		}
		return "";
	}

	/**
	 * restituisce la stringa che rappresentazione del lambda termine pulito dal
	 * tipo
	 * @param t termine
	 * @return stringa che rappresentazione del lambda termine pulito dal tipo
	 */
	private String termToStringLci(Term t) {

		if (t instanceof Var) {
			return ((Var) t).getName();
		} else if (t instanceof Abs) {
			return "(\\" + (((Abs) t).getParam()) + "."
					+ termToStringLci(((Abs) t).getBody()) + ")";
		} else if (t instanceof App) {
			return "(" + termToStringLci(((App) t).getT1()) + " "
					+ termToStringLci(((App) t).getT2()) + ")";
		} else if (t instanceof Uq) {
			return termToStringLci(((Uq) t).getTerm());
		} else if (t instanceof Tapp) {
			return termToStringLci(((Tapp) t).getTerm());
		} else if (t instanceof Nrif) {
			return termToStringLci(((Nrif) t).getNome().getTerm());
		} else if (t instanceof Let) {
			return "((\\" + (((Let) t).getVar()) + "."
					+ termToStringLci(((Let) t).getT2()) + ") "
					+ termToStringLci(((Let) t).getT1()) + ")";
		}
		return "";
	}

	/**
	 * restituisce la stringa che rappresenta il tipo
	 * @param t : t il tipo
	 * @return a stringa che rappresenta il tipo
	 */
	private String typeToString(T t) {
		if (t instanceof T1) {
			String p = ((T1) t).getP();
			EList<T> c = ((T1) t).getC();
			if (!c.isEmpty()) {
				p += "->";
				for (T a : c)
					p += typeToString(a);
			}
			return p;
		} else if (t instanceof T2) {
			String p = "(" + typeToString(((T2) t).getP1()) + ")";
			EList<T> c = ((T2) t).getC1();
			if (!c.isEmpty()) {
				p += "->";
				for (T a : c)
					p += typeToString(a);
			}
			return p;

		} else if (t instanceof T3) {
			return "forall " + (((T3) t).getVar()) + " "
					+ typeToString(((T3) t).getT());

		} else if (t instanceof Trif) {
			return typeToString(((Trif) t).getNome().getType());

		}
		return "";

	}	
	

	private String  typeTerm() throws Exception {

		String userhome = System.getProperty("user.home");
		File f = new File(userhome + "/util/typeDlal/type");
		 HashMap<String, Double> map = getVarValue();
		String temp = "";
		if (!f.exists())
			throw new Exception("file not found");

		FileReader fr = new FileReader(f);
		int c = fr.read();
		while (c != -1) {
			if (c == 65533) {
				fr.read();
				char b = (char) fr.read();
				if (b == 'F') {
					fr.read();
					String var =""+((char)fr.read())+""+((char)fr.read());
					double n = map.get(var);
					int nv = (int) n;
					for(int i=0; i<nv; i++)
					    temp+="§";
					fr.read();
				} else if (b == 'T') {
					fr.read();
					String var =""+((char)fr.read())+""+((char)fr.read());
					double n = map.get(var);
					int nv = (int) n;
					for(int i=0; i<nv; i++)
					    temp+="!";
					fr.read();
				} else {
					String var = b+""+((char)fr.read());
					double v = map.get(var);
					int nv = (int)v;
					for(int i=0; i<nv; i++)
						temp+="§";
					fr.read();
				}

			} else
				temp += " " + (char) c;
			    c = fr.read();
		}
		return temp;
	}

	
	public  HashMap<String,Double> getVarValue(){
		HashMap<String, Double> map = new HashMap<String, Double>();
		String userhome = System.getProperty("user.home");
		GlpkSolver solver = new GlpkSolver();
		GlpkSolver glp = GlpkSolver.readCpxlp(userhome+"/util/typeDlal/constraint");
		glp.integer();
		for(int i=1; i<=glp.getNumCols(); i++){
			map.put(glp.getColName(i), glp.mipColVal(i));		
		}			
		return map;
	}	
	

	
	
	
	/* Fine SERVICE METHODS */
	

	/**
	 * controlla se esiste la cartella util contenente inf_dlal , se non esiste
	 * la crea e chiede all'untente di agiungere inf_dlal
	 * 
	 * @return boolean , true se esiste la cartella util e contiene inf_dlal ,
	 *         false altrimenti
	 * @throws IOException
	 */
	private boolean checkUtility() throws IOException {
		String path = System.getProperty("user.home");
		path += "/util";
		File dir = new File(path);
		if (dir.exists()) {

			if (!new File(path + "/typeDlal").exists())
				new File(path + "/typeDlal").mkdir();

			if (!new File(path + "/lci").exists())
				new File(path + "/lci").mkdir();

			if (!new File(path + "/latex").exists())
				new File(path + "/latex").mkdir();

			File dlal = new File(path + "/inf_dlal");
			if (dlal.exists()) {
				File typedlal = new File(path + "/inf_dlal/type_dlal");
				if (!typedlal.exists())
					return false;
				else
					return true;
			} else
				return false;

		} else {
			dir.mkdir();
			new File(path + "/typeDlal").mkdir();
			new File(path + "/lci").mkdir();
			new File(path + "/latex").mkdir();
			return false;
		}

	}

	public static void main(String a[]){
		System.out.println(new Interprete().getVarValue());
	}
	
	
}
